﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing.Design;
using System.Windows.Forms.Design;
using System.ComponentModel;
using System.Windows.Forms;
using System.Drawing;

using Noris.Schedule.Support.Components;
using Noris.Schedule.Support;
using Noris.Tools.FrxEditor.Components;

namespace Noris.Tools.FrxEditor.Editor
{
    #region class CheckBoxEditor : UI editor v PropertyGridu, který v řádku PropertyGridu zobrazí CheckBox pro danou property
    /// <summary>
    /// CheckBoxEditor : UI editor v PropertyGridu, který v řádku PropertyGridu zobrazí CheckBox pro danou property
    /// </summary>
    public class CheckBoxEditor : System.Drawing.Design.UITypeEditor
    {
        /// <summary>
        /// Gets the editor style used by the System.Drawing.Design.UITypeEditor.EditValue(System.IServiceProvider,System.Object) method.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
        {
            return UITypeEditorEditStyle.None;
        }
        /// <summary>
        /// Edits the specified object's value using the editor style indicated by the System.Drawing.Design.UITypeEditor.GetEditStyle() method.
        /// </summary>
        /// <param name="context"></param>
        /// <param name="provider"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
        {
            return base.EditValue(context, provider, value);
        }
        /// <summary>
        /// Indicates whether the specified context supports painting a representation of an object's value within the specified context.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override bool GetPaintValueSupported(ITypeDescriptorContext context)
        {
            return true;
        }
        /// <summary>
        /// Určí, zda DropDown oblast má být resizovatelná
        /// </summary>
        public override bool IsDropDownResizable
        {
            get
            {
                return false;
            }
        }
        /// <summary>
        /// Vykreslí hodnotu do daného prostoru
        /// </summary>
        /// <param name="e"></param>
        public override void PaintValue(PaintValueEventArgs e)
        {
            bool value = (bool)e.Value;
            if (value)
                e.Graphics.FillRectangle(Brushes.LightBlue, e.Bounds);
            else
                e.Graphics.FillRectangle(Brushes.LightSalmon, e.Bounds);
        }
    }
    #endregion
    #region class PropertyEditorOpenForm : UI editor v PropertyGridu, který v řádku PropertyGridu zobrazí button pro otevření formuláře
    /// <summary>
    /// PropertyEditorOpenForm : UI editor v PropertyGridu, který v řádku PropertyGridu zobrazí button pro otevření formuláře.
    /// Při použití buttonu se vyhodnotí instance a zobrazená property, a otevře se formulář - přes interface IPropertyOpenForm.
    /// </summary>
    public class PropertyEditorOpenForm : System.Drawing.Design.UITypeEditor
    {
        /// <summary>
        /// Gets the editor style used by the System.Drawing.Design.UITypeEditor.EditValue(System.IServiceProvider,System.Object) method.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
        {
            return System.Drawing.Design.UITypeEditorEditStyle.Modal;
        }
        /// <summary>
        /// Edits the specified object's value using the editor style indicated by the System.Drawing.Design.UITypeEditor.GetEditStyle() method.
        /// </summary>
        /// <param name="context"></param>
        /// <param name="provider"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
        {
            if (context.Instance is object[])
            {   /* je vybráno více objektů - co s tím? */   }
            else
            {   // Jeden objekt: pokud umí IPropertyOpenForm, zavolám ho:
                IPropertyOpenForm source = context.Instance as IPropertyOpenForm;
                if (source != null)
                {
                    source.PropertyGridOpenForm(context, value);
                }
            }
            return value;
        }
    }
    #endregion
    #region class PropertyEditorGetFile : UI editor pro výběr konkrétního souboru do property v PropertyGridu
    /// <summary>
    /// PropertyEditorGetFile : UI editor pro výběr konkrétního souboru do property v PropertyGridu.
    /// Třída, která u některé své property deklaruje tento editor
    /// { u dané property má atribut: [Editor(typeof(PropertyEditorGetFile), typeof(System.Drawing.Design.UITypeEditor))] }
    /// by měla mít interface IPropertyDataSource, a v metodě IPropertyDataSource.GetPropertyData() 
    /// by pro tuto property měla umět vrátit hodnotu pro klíč PropertyEditorGetFile.GET_FILTER (vrátí filtr do dialogu).
    /// </summary>
    public class PropertyEditorGetFile : System.Drawing.Design.UITypeEditor
    {
        /// <summary>
        /// Gets the editor style used by the System.Drawing.Design.UITypeEditor.EditValue(System.IServiceProvider,System.Object) method.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
        {
            return UITypeEditorEditStyle.Modal;
        }
        /// <summary>
        /// Edits the specified object's value using the editor style indicated by the System.Drawing.Design.UITypeEditor.GetEditStyle() method.
        /// </summary>
        /// <param name="context"></param>
        /// <param name="provider"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
        {
            string file = "";
            string path = "";
            if (value is string)
            {
                string fileName = (string)value;
                if (!String.IsNullOrEmpty(fileName))
                {
                    file = System.IO.Path.GetFileName(fileName);
                    path = System.IO.Path.GetDirectoryName(fileName);
                }
            }

            // Zkusím získat z instance některé další hodnoty:
            string filter = this.GetItem<string>(context, GET_FILTER);
            bool selected = SystemFileDialog.GetExistingFile(ref file, ref path, filter, "*.*", "Najdi soubor");
            return file;
        }
        /// <summary>
        /// Klíčové slovo pro získání filtru do dialogu GetFile
        /// </summary>
        public const string GET_FILTER = "Filter";
        /// <summary>
        /// Pro daný objekt (instance) a property vrátí hodnotu získanou z instance, pro daný název hodnoty
        /// </summary>
        /// <param name="context">Odkaz na kontext PropertyGridu</param>
        /// <param name="dataName">Klíč, jehož data hledáme</param>
        /// <returns></returns>
        private T GetItem<T>(ITypeDescriptorContext context, string dataName)
        {
            object instance = context.Instance;
            PropertyDescriptor propertyDesc = context.PropertyDescriptor;
            IPropertyDataSource source = instance as IPropertyDataSource;
            if (source != null)
            {
                object value = source.GetPropertyData(propertyDesc, dataName);
                if (value is T)
                    return (T)value;
            }
            return default(T);
        }
    }
    #endregion
    #region class ListBoxEditor : UI Editor v PropertyGridu, který zobrazí dynamicky sestavovanou nabídku položek (ListBox) pro konkrétní objekt a jeho property
    /// <summary>
    /// <para>
    /// ListBoxEditor : UI Editor v PropertyGridu, který zobrazí dynamicky sestavovanou nabídku položek (ListBox) pro konkrétní objekt a jeho property.
    /// </para>
    /// <para>
    /// Aby se v PropertyGridu u této property mohla zobrazovat nabídku položek, musí její instance (kde je property umístěna) implementovat interface IPropertyListSource.
    /// Pak v metodě IPropertyListSource.GetPropertyListItems(PropertyDescriptor propertyDesc) tato instance musí sestavit soupis aktuálních položek nabídky a vrátit jej.
    /// Tyto položky pak PropertyGrid nabídne v DropDownu.
    /// </para>
    /// </summary>
    public class ListBoxEditor : System.Drawing.Design.UITypeEditor
    {
        /// <summary>
        /// Určuje, v jakém režimu bude probíhat editace hodnoty (DropDown / Form)
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
        {
            return UITypeEditorEditStyle.DropDown;
        }
        /// <summary>
        /// Určí, zda dropdown je resizovatelný
        /// </summary>
        public override bool IsDropDownResizable { get { return true; } }
        /// <summary>
        /// Zajišťuje vlastní editaci hodnoty
        /// </summary>
        /// <param name="context"></param>
        /// <param name="provider"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
        {
            string text = (value == null ? "" : value.ToString());

            IWindowsFormsEditorService edSvc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
            if (edSvc != null)
            {
                using (ListBoxOk lbo = new ListBoxOk(edSvc))
                {
                    lbo.Items = this.GetItems(context);
                    lbo.SelectedItem = text;
                    edSvc.DropDownControl(lbo);
                    if (lbo.DialogOk)
                        text = lbo.SelectedItem;
                }
            }
            return text;
        }
        /// <summary>
        /// Pro daný objekt (instance) a property vrátí seznam hodnot do nabídky Listboxu
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private IEnumerable<string> GetItems(ITypeDescriptorContext context)
        {
            PropertyDescriptor propertyDesc = context.PropertyDescriptor;
            if (context.Instance is object[])
            {
                Dictionary<string, object> result = new Dictionary<string, object>();
                object[] instances = context.Instance as object[];
                foreach (object instance in instances)
                {
                    IPropertyListSource source = instance as IPropertyListSource;
                    if (source != null)
                    {
                        IEnumerable<string> items = source.GetPropertyListItems(propertyDesc);
                        if (items != null)
                        {
                            foreach (string item in items)
                            {
                                if (!String.IsNullOrEmpty(item) && !result.ContainsKey(item))
                                    result.Add(item, null);
                            }
                        }
                    }
                }
                return result.Keys.ToArray();
            }
            else
            {
                IPropertyListSource source = context.Instance as IPropertyListSource;
                if (source != null)
                    return source.GetPropertyListItems(propertyDesc);
                return new string[0];
            }
        }
        /// <summary>
        /// Určuje, zda editor provádí vykreslování hodnoty do PropertyGridu
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override bool GetPaintValueSupported(ITypeDescriptorContext context)
        {
            return base.GetPaintValueSupported(context);
        }
        /// <summary>
        /// Vykreslí aktuální hodnotu do PropertyGridu
        /// </summary>
        /// <param name="e"></param>
        public override void PaintValue(PaintValueEventArgs e)
        {
            base.PaintValue(e);
        }
    }
    #endregion
    #region class ListBoxOk : Třída (potomek PropertyEditorOk : UserControl), která nabízí ListBox + tlačítka OK + Storno
    /// <summary>
    /// ListBoxOk : Třída (potomek PropertyEditorOk : UserControl), která nabízí ListBox + tlačítka OK + Storno
    /// </summary>
    public class ListBoxOk : PropertyEditorOk
    {
        #region Konstrukce a vnitřní události
        /// <summary>
        /// Třída, která nabízí ListBox + tlačítka OK + Storno.
        /// Konstruktor.
        /// </summary>
        public ListBoxOk()
            : base()
        {
            this._InitControls();
        }
        /// <summary>
        /// Třída, která nabízí ListBox + tlačítka OK + Storno.
        /// Konstruktor.
        /// </summary>
        public ListBoxOk(IWindowsFormsEditorService editorSvc)
            : base(editorSvc)
        {
            this._InitControls();
        }
        private void _InitControls()
        {
            this._ListBox = new ListBox() { BorderStyle = System.Windows.Forms.BorderStyle.None, BackColor = SystemColors.ControlLightLight };
            this._ListBox.DoubleClick += new EventHandler(_ListBox_DoubleClick);
            this._ListBox.PreviewKeyDown += new PreviewKeyDownEventHandler(_ListBox_PreviewKeyDown);
            this._ListBox.KeyDown += new KeyEventHandler(_ListBox_KeyDown);
            this.Controls.Add(this._ListBox);
            this.SetUserLayout();
        }

        void _ListBox_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                e.IsInputKey = true;
                this.CloseControl(true);
            }
        }

        void _ListBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                e.Handled = true;
                this.CloseControl(true);
            }
        }
        void _ListBox_DoubleClick(object sender, EventArgs e)
        {
            this.CloseControl(true);
        }
        private ListBox _ListBox;
        #endregion
        #region Override
        /// <summary>
        /// Třída potomka zde upraví pozice svých controlů, a to do prostoru this.UserRectangle.
        /// </summary>
        protected override void SetUserLayout()
        {
            this._ListBox.Bounds = this.UserRectangle;
        }
        #endregion
        #region Public property
        /// <summary>
        /// Seznam položek v nabídce
        /// </summary>
        public IEnumerable<string> Items
        {
            get
            {
                List<string> items = new List<string>();
                foreach (object item in this._ListBox.Items)
                    items.Add(item.ToString());
                return items;
            }
            set
            {
                this._ListBox.Items.Clear();
                if (value != null)
                {
                    foreach (string item in value)
                        this._ListBox.Items.Add(item);
                }
            }
        }
        /// <summary>
        /// Aktuální položka
        /// </summary>
        public string SelectedItem
        {
            get { return (this._ListBox.SelectedItem == null ? null : this._ListBox.SelectedItem.ToString()); }
            set
            {
                int cnt =  this._ListBox.Items.Count;
                if (cnt > 0)
                    this._ListBox.SelectedIndex = 0;

                for (int i = 0; i < cnt; i++)
                {
                    if (this._ListBox.Items[i].ToString() == value)
                    {
                        this._ListBox.SelectedIndex = i;
                        break;
                    }
                }
            }
        }
        #endregion
    }
    #endregion
    #region class PropertyEditorOk : Třída (UserControl), která podporuje UI Editor, nabízí UserArea + tlačítka OK + Storno. Předek pro konkrétní editory (Listbox, atd).
    /// <summary>
    /// PropertyEditorOk : Třída (UserControl), která podporuje UI Editor, nabízí UserArea + tlačítka OK + Storno. Předek pro konkrétní editory (Listbox, atd).
    /// </summary>
    public class PropertyEditorOk : UserControl
    {
        #region Konstrukce a vnitřní události
        /// <summary>
        /// Třída, která nabízí ListBox + tlačítka OK + Storno.
        /// Konstruktor.
        /// </summary>
        public PropertyEditorOk()
        {
            this._InitControls();
        }
        /// <summary>
        /// Třída, která nabízí ListBox + tlačítka OK + Storno.
        /// Konstruktor.
        /// </summary>
        public PropertyEditorOk(IWindowsFormsEditorService editorSvc)
        {
            this._InitControls();
            this._EditorSvc = editorSvc;
        }
        /// <summary>
        /// Dispose
        /// </summary>
        /// <param name="disposing"></param>
        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            this._EditorSvc = null;
        }
        /// <summary>
        /// Iniciace vnitřních dat
        /// </summary>
        private void _InitControls()
        {
            this.DialogOk = false;

            this._Ok = new DButton() { Text = "OK", UseCompatibleTextRendering = true, FlatStyle = FlatStyle.Standard, Font = SystemFonts.DefaultFont, Size = new Size(46, 21), TabStop = false };
            this._Close = new DButton() { Text = "Storno", UseCompatibleTextRendering = true, FlatStyle = FlatStyle.Standard, Font = SystemFonts.DefaultFont, Size = new Size(46, 21), TabStop = false };
            this._Ok.Click += new EventHandler(_Ok_Click);
            this._Close.Click += new EventHandler(_Close_Click);
            this.Controls.Add(this._Ok);
            this.Controls.Add(this._Close);
            this.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
            this.BackColor = SystemColors.ControlLight;
            this.Size = new Size(150, 120);
            this.SetInnerLayout();
            this.SizeChanged += new EventHandler(_PropertyEditorOk_SizeChanged);
        }
        void _PropertyEditorOk_SizeChanged(object sender, EventArgs e)
        {
            this.SetInnerLayout();
            this.SetUserLayout();
        }
        void _Ok_Click(object sender, EventArgs e)
        {
            if (this.CheckOk())
                this.CloseControl(true);
        }
        void _Close_Click(object sender, EventArgs e)
        {
            this.CloseControl(false);
        }
        /// <summary>
        /// Akceptuje rozměry controlu a podle nich rozmístí svoje vnitřní controly a nastaví souřadnice this.UserRectangle.
        /// Nevolá this.SetUserLayout(); to je virtuální metoda (nesmí se volat z konstruktoru).
        /// </summary>
        private void SetInnerLayout()
        {
            Rectangle bound = this.ClientRectangle;
            int x = bound.Width - this._Ok.Width - this._Close.Width - 5;
            int y = bound.Y + bound.Height - this._Ok.Height - 1;
            this._Ok.Location = new Point(x, y);
            x += this._Ok.Width + 3;
            this._Close.Location = new Point(x, y);
            this.UserRectangle = new Rectangle(bound.X + 1, bound.Y + 1, bound.Width - 2, y);
        }
        private DButton _Ok;
        private DButton _Close;
        private IWindowsFormsEditorService _EditorSvc;
        #endregion
        #region Protected metody a property - něco pro potomstvo
        /// <summary>
        /// Třída potomka zde upraví pozice svých controlů, a to do prostoru this.UserRectangle.
        /// </summary>
        protected virtual void SetUserLayout()
        { }
        /// <summary>
        /// Třída potomka zde zkontroluje, zda je možno ukončit dialog OK = true.
        /// Pokud se vrátí false, pak tlačítko OK nereaguje.
        /// Bázová metoda vrací true.
        /// </summary>
        /// <returns></returns>
        protected virtual bool CheckOk()
        {
            return true;
        }
        /// <summary>
        /// Třída potomka zde najde souřadnice prostoru, do něhož si může umisťovat svoje controly.
        /// Využije se v metodě SetUserLayout().
        /// </summary>
        protected Rectangle UserRectangle { get; private set; }
        /// <summary>
        /// Tato metoda zajistí zavření controlu, nastaví výsledek dialogu : this.DialogOk = parametr.
        /// </summary>
        /// <param name="dialogOk"></param>
        protected void CloseControl(bool dialogOk)
        {
            this.DialogOk = dialogOk;
            if (this._EditorSvc != null)
                this._EditorSvc.CloseDropDown();
            else
                this.Hide();
        }

        #endregion
        #region Public property
        /// <summary>
        /// Příznak true, pokud dialog byl potvrzen OK; false pokud bylo Storno nebo opuštění controlu.
        /// </summary>
        public bool DialogOk { get; private set; }
        #endregion
    }
    #endregion
    #region interface IPropertyListSource : garantuje přítomnost metody GetPropertyListItems()
    /// <summary>
    /// Interface IPropertyListSource garantuje přítomnost metody GetPropertyListItems(), která z aktuálního objektu vytvoří a vrátí seznam dostupných hodnot pro danou property.
    /// Vrácený seznam se pak používá pro takové property, které mají uvedený editor ListBoxEditor.
    /// </summary>
    public interface IPropertyListSource
    {
        /// <summary>
        /// Metoda, která v aktuální instanci (kde je implementovaná) a pro určenou property (parametr propertyDesc) najde a vrátí soupis položek do nabídky DropDownListu.
        /// </summary>
        /// <param name="propertyDesc">Informace o property, která se právě edituje.</param>
        /// <returns>Vrací seznam položek do nabídky DropDownListu</returns>
        IEnumerable<string> GetPropertyListItems(PropertyDescriptor propertyDesc);
    }
    #endregion
    #region interface IPropertyDataSource : garantuje přítomnost metody GetPropertyData()
    /// <summary>
    /// Interface IPropertyDataSource garantuje přítomnost metody GetPropertyData(), která z aktuálního objektu zjistí a vrátí konkrétní data pro danou property.
    /// </summary>
    public interface IPropertyDataSource
    {
        /// <summary>
        /// Metoda, která v konkrétní instanci najde a vrátí seznam dostupných hodnot pro danou property.
        /// </summary>
        /// <param name="propertyDesc">Informace o property, která se právě edituje.</param>
        /// <param name="dataName">Název údaje, který potřebuje editor zjistit</param>
        /// <returns>Vrací zjištěnou hodnotu</returns>
        object GetPropertyData(PropertyDescriptor propertyDesc, string dataName);
    }
    #endregion
    #region interface IPropertyOpenForm : garantuje přítomnost metody PropertyGridOpenForm()
    /// <summary>
    /// Interface IPropertyOpenForm garantuje přítomnost metody PropertyGridOpenForm(), která otevře formulář pro editaci pro danou property.
    /// </summary>
    public interface IPropertyOpenForm
    {
        /// <summary>
        /// Otevře formulář pro editaci dané property
        /// </summary>
        /// <param name="context"></param>
        /// <param name="value"></param>
        void PropertyGridOpenForm(ITypeDescriptorContext context, object value);
    }
	#endregion
}
